<?php
declare(strict_types=1);

namespace SixShop\Wechat\Cron;

use app\api\controller\Wechat;
use EasyWeChat\Kernel\Contracts\AccessToken as AccessTokenInterface;
use SixShop\Core\Attribute\Cron;
use SixShop\Core\Job\BaseJob;
use SixShop\System\Job\ClosureJob;
use SixShop\Wechat\Log;
use SixShop\Wechat\Service\MiniApp;
use think\facade\Cache;
use function Symfony\Component\String\s;

class AccessTokenCron extends BaseJob
{
    /**
     * 提前 60 秒执行
     */
    private const int LEAD_TIME = 60;

    private AccessTokenInterface $accessToken;

    private string $cacheKey;

    private bool $isRunning = false;

    /**
     * 刷新 AccessToken 的计划执行时间
     */
    private int $scheduleRefreshTime = 0;

    public function __construct(private MiniApp $miniApp, private Log $log)
    {
        $this->accessToken = $this->miniApp->getAccessToken();
        $this->cacheKey = Cache::getCachekey($this->accessToken->getKey());
    }

    #[Cron('0 0 */2 * * *', 'wechat.refreshMiniAppAccessToken')]
    public function refreshMiniAppAccessToken(): void
    {
        if (time() > $this->scheduleRefreshTime) {
            $expireIn = $this->execute();
            self::dispatch()->delay($expireIn - self::LEAD_TIME);
            $this->scheduleRefreshTime = time() + $expireIn - self::LEAD_TIME;
            $this->log->debug('定时刷新小程序 AccessToken 任务已启动，计划执行时间：{time}', [
                'time' => date('Y-m-d H:i:s', $this->scheduleRefreshTime)
            ]);
        }
    }

    public function execute(): int
    {
        if ($this->isRunning) {
            return Cache::ttl($this->cacheKey);
        }
        $this->isRunning = true;
        try {
            $oldToken = $this->accessToken->getToken();
            $newToken = $this->accessToken->refresh();
            $expireIn = Cache::ttl($this->cacheKey);
            $this->log->debug('刷新小程序 AccessToken 成功，过期时间：{time}, 结果：{result}', [
                'time' => date('Y-m-d H:i:s', time() + $expireIn),
                'result' => $oldToken === $newToken ? '未更新' : '已更新',
            ]);
        } finally {
            $this->isRunning = false;
        }
        return $expireIn;
    }
}